﻿using System;
using System.IO;
using System.Runtime.InteropServices;

namespace AlarmClock.BLL
{

    public class PPWin32
    {
        #region 公共方法

        #region 窗体显示效果

        /// <summary>
        /// 窗体动画函数
        /// </summary>
        /// <param name="whnd">指定产生动画的窗口的句柄</param>
        /// <param name="dwtime">指定动画持续的时间</param>
        /// <param name="dwflag">指定动画类型，可以是一个或多个标志的组合。</param>
        /// <returns></returns>
        [DllImport("user32.dll")]//导入dll
        public static extern bool AnimateWindow(IntPtr whnd, int dwtime, int dwflag);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int SetClassLong(IntPtr hwnd, int nIndex, int dwNewLong);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int GetClassLong(IntPtr hwnd, int nIndex);

        public const int GCL_HICON = -14;
        public const int GCL_HICONSM = -34;
        public const int CS_DropSHADOW = 0x20000;
        public const int GCL_STYLE = (-26);

        //dwflag的取值如下 ,根据不同的动画效果声明自己需要的
        //从左到右显示 该标志可以在滚动动画和滑动动画中使用。使用AW_CENTER标志时忽略该标志
        public const Int32 AW_HOR_POSITIVE = 0x0001;

        //从右到左显示 该标志可以在滚动动画和滑动动画中使用。使用AW_CENTER标志时忽略该标志
        public const Int32 AW_HOR_NEGATIVE = 0x0002;

        //从上到下显示 该标志可以在滚动动画和滑动动画中使用。使用AW_CENTER标志时忽略该标志
        public const Int32 AW_VER_POSITIVE = 0x0004;

        //从下到上显示 该标志可以在滚动动画和滑动动画中使用。使用AW_CENTER标志时忽略该标志
        public const Int32 AW_VER_NEGATIVE = 0x0008;

        //若使用了AW_HIDE标志，则使窗口向内重叠，即收缩窗口；否则使窗口向外扩展，即展开窗口
        public const Int32 AW_CENTER = 0x00010;

        //隐藏窗口，缺省则显示窗口
        public const Int32 AW_HIDE = 0x10000;

        //激活窗口，在使用了AW_HIDE标志后不要使用这个标志
        public const Int32 AW_ACTIVATE = 0x20000;

        //使用滑动类型。缺省则为滚动动画类型。当使用AW_CENTER标志时，这个标志就被忽略
        public const Int32 AW_SLIDE = 0x40000;

        //透明度从高到低
        public const Int32 AW_BLEND = 0x80000;

        #endregion

        #endregion


        /// <summary>
        /// 该函数将创建指定窗口的线程设置到前台，并且激活该窗口。键盘输入转向该窗口，并为用户改各种可视的记号。
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns>如果窗口设入了前台，返回值为非零；如果窗口未被设入前台，返回值为零</returns>
        [DllImport("user32.dll ")]
        public static extern bool SetForegroundWindow(IntPtr hWnd);

        /// <summary>
        /// 该函数设置由不同线程产生的窗口的显示状态
        /// </summary>
        /// <param name="hWnd">窗口句柄</param>
        /// <param name="nCmdShow">指定窗口如何显示。查看允许值列表，请查阅ShowWlndow函数的说明部分</param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
        [DllImport("user32.dll")]
        public static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);

        //根据任务栏应用程序显示的名称找窗口的名称
        [DllImport("User32.dll", EntryPoint = "FindWindow")]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
        // 窗口显示模式的常量
        public const int SW_HIDE = 0; // 隐藏窗口
        public const int SW_SHOW = 5; // 显示窗口
        private const int SW_SHOWNOMAL = 1;
        public const int SW_MAXIMIZE = 3; // 最大化窗口
        public const int SW_MINIMIZE = 6; // 最小化窗口
        public const int SW_RESTORE = 9; // 恢复窗口

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern int SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int Width, int Height, int flags);
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern System.IntPtr GetForegroundWindow();

    }

    #region MCIPlayer声音播放器类

    public class MCIPlayer : IDisposable
    {
        /// <summary>
        /// 文件长路径转短（API需要）
        /// </summary>
        /// <param name="longPath">文件长路径</param>
        /// <param name="buffer">null</param>
        /// <param name="bufferSize">0</param>
        /// <returns></returns>
        [DllImport("kernel32.dll", EntryPoint = "GetShortPathNameW", CharSet = CharSet.Unicode)]
        extern static short GetShortPath(
            string longPath,
            string buffer,
            int bufferSize
            );

        /// <summary>
        /// MCI命令【关键API】
        /// </summary>
        /// <param name="command">MCI命令字符串</param>
        /// <param name="buffer">存放反馈信息的缓冲区</param>
        /// <param name="bufferSize">缓冲区的长度</param>
        /// <param name="callback">回调窗口的句柄，一般为NULL</param>
        /// <returns></returns>
        [DllImport("winmm.dll", EntryPoint = "mciSendString", CharSet = CharSet.Unicode)]
        extern static int MciSendString(
            string command,  //MCI命令字符串
            string buffer,   //存放反馈信息的缓冲区
            int bufferSize,  //缓冲区的长度
            IntPtr callback  //回调窗口的句柄，一般为NULL
            );


        bool TryGetShortPath(string longPath, out string shortPath)
        {
            shortPath = null;
            short reqLen = GetShortPath(longPath, null, 0);   // 指定 null与0, 则返回需要的长度
            if (reqLen == 0)
                return false;

            shortPath = new string('\0', reqLen);   // 声明缓冲

            short rstLen = GetShortPath(longPath, shortPath, reqLen);   // 转换
            if (rstLen == 0 || rstLen == reqLen)
                return false;

            shortPath = shortPath.TrimEnd('\0');
            return true;
        }
        private string longpath;
        private string shortName;
        private string aliasName;

        public MCIPlayer(string path)
        {
            if (!File.Exists(path))
                throw new FileNotFoundException("File not exist.", path);

            longpath = path;
        }

        void MciSendStringWithCheck(string command, string buffer, int bufferSize, IntPtr callback)
        {
            int err = MciSendString(command, buffer, bufferSize, callback);
            if (err != 0)
                throw new MciException(err);
        }

        string StatusInfo(string info)
        {
            string buffer = new string('\0', 32);
            MciSendStringWithCheck($"status {aliasName} {info}", buffer, 32, IntPtr.Zero);

            return buffer.TrimEnd('\0');
        }

        public string DevicePath { get => longpath; }
        public string DeviceShortPath { get => shortName; }
        public string AliasName { get => aliasName; }


        public bool SetDevicePath(string longpath)
        {
            if (aliasName != null)
                return false;

            this.longpath = longpath;
            return true;
        }

        public void SetVolume(int volme)
        {
            MciSendStringWithCheck($"setaudio {aliasName} volume to{volme} ", null, 0, IntPtr.Zero);
        }

        public void Open()
        {
            if (!TryGetShortPath(longpath, out shortName))
                throw new Exception("初始化获取短文件名失败！");

            aliasName = $"curMCIName{DateTime.Now.Ticks}";
            MciSendStringWithCheck($"open {shortName} alias {aliasName}", null, 0, IntPtr.Zero);
        }
        public void Close()
        {
            MciSendStringWithCheck($"close {aliasName}", null, 0, IntPtr.Zero);

            aliasName = null;
        }

        public void Play()
        {
            MciSendStringWithCheck($"play {aliasName}", null, 0, IntPtr.Zero);
        }

        public void Resume()
        {
            MciSendStringWithCheck($"resume {aliasName}", null, 0, IntPtr.Zero);
        }
        public void Pause()
        {
            MciSendStringWithCheck($"pause {aliasName}", null, 0, IntPtr.Zero);
        }
        public void Stop()
        {
            MciSendStringWithCheck($"stop {aliasName}", null, 0, IntPtr.Zero);
        }

        public int GetPosition()
        {
            return int.Parse(StatusInfo("position"));
        }
        public int GetLength()
        {
            return int.Parse(StatusInfo("length"));
        }

        public void Dispose()
        {
            if (aliasName != null)
                Close();
        }

        public PlaybackState GetState()
        {
            switch (StatusInfo("mode").ToLower())
            {
                case "playing":
                    return PlaybackState.Playing;
                case "paused":
                    return PlaybackState.Paused;
                case "stopped":
                    return PlaybackState.Stopped;
                default:
                    return PlaybackState.Invalid;
            }
        }

        public void PlayWait()
        {
            MciSendStringWithCheck($"play {aliasName} wait", null, 0, IntPtr.Zero);
        }

        public void PlayRepeat()
        {
            MciSendStringWithCheck($"play {aliasName} repeat", null, 0, IntPtr.Zero);
        }

        public void Seek(int position)
        {
            MciSendStringWithCheck($"seek {aliasName} to {position}", null, 0, IntPtr.Zero);
        }

        public void SeekToStart()
        {
            MciSendStringWithCheck($"seek {aliasName} to start", null, 0, IntPtr.Zero);
        }
        public void SetSeekMode(bool fExact)
        {
            MciSendStringWithCheck($"set {aliasName} seek exactly {(fExact ? "on" : "off")}", null, 0, IntPtr.Zero);
        }

    }
    public enum PlaybackState
    {
        Stopped,
        Playing,
        Paused,

        Invalid = -1,
    }
    public class MciException : Exception
    {
        private MciError err;

        public int ErrorId { get => (int)err; }
        public string ErrorName { get => err.ToString(); }
        public override string Message { get => ErrorName; }
        public MciException() { }
        public MciException(int errorId)
        {
            if (Enum.IsDefined(typeof(MciError), errorId))
                err = (MciError)errorId;
            else
                throw new ArgumentOutOfRangeException("不是正确的错误ID");
        }
        public MciException(string errorName)
        {
            if (!Enum.TryParse<MciError>(errorName, out err))
                throw new ArgumentOutOfRangeException("不是正确的错误ID");
        }
        enum MciError
        {
            MCIERR_NO_ERROR = 0,

            MCIERR_INVALID_DEVICE_ID = 257,
            MCIERR_UNRECOGNIZED_KEYWORD = 259,
            MCIERR_UNRECOGNIZED_COMMAND = 261,
            MCIERR_HARDWARE = 262,
            MCIERR_INVALID_DEVICE_NAME = 263,
            MCIERR_OUT_OF_MEMORY = 264,
            MCIERR_DEVICE_OPEN = 265,
            MCIERR_CANNOT_LOAD_DRIVER = 266,
            MCIERR_MISSING_COMMAND_STRING = 267,
            MCIERR_PARAM_OVERFLOW = 268,
            MCIERR_MISSING_STRING_ARGUMENT = 269,
            MCIERR_BAD_INTEGER = 270,
            MCIERR_PARSER_INTERNAL = 271,
            MCIERR_DRIVER_INTERNAL = 272,
            MCIERR_MISSING_PARAMETER = 273,
            MCIERR_UNSUPPORTED_FUNCTION = 274,
            MCIERR_FILE_NOT_FOUND = 275,
            MCIERR_DEVICE_NOT_READY = 276,
            MCIERR_INTERNAL = 277,
            MCIERR_DRIVER = 278,
            MCIERR_CANNOT_USE_ALL = 279,
            MCIERR_MULTIPLE = 280,
            MCIERR_EXTENSION_NOT_FOUND = 281,
            MCIERR_OUTOFRANGE = 282,
            MCIERR_FLAGS_NOT_COMPATIBLE = 284,
            MCIERR_FILE_NOT_SAVED = 286,
            MCIERR_DEVICE_TYPE_REQUIRED = 287,
            MCIERR_DEVICE_LOCKED = 288,
            MCIERR_DUPLICATE_ALIAS = 289,
            MCIERR_BAD_CONSTANT = 290,
            MCIERR_MUST_USE_SHAREABLE = 291,
            MCIERR_MISSING_DEVICE_NAME = 292,
            MCIERR_BAD_TIME_FORMAT = 293,
            MCIERR_NO_CLOSING_QUOTE = 294,
            MCIERR_DUPLICATE_FLAGS = 295,
            MCIERR_INVALID_FILE = 296,
            MCIERR_NULL_PARAMETER_BLOCK = 297,
            MCIERR_UNNAMED_RESOURCE = 298,
            MCIERR_NEW_REQUIRES_ALIAS = 299,
            MCIERR_NOTIFY_ON_AUTO_OPEN = 300,
            MCIERR_NO_ELEMENT_ALLOWED = 301,
            MCIERR_NONAPPLICABLE_FUNCTION = 302,
            MCIERR_ILLEGAL_FOR_AUTO_OPEN = 303,
            MCIERR_FILENAME_REQUIRED = 304,
            MCIERR_EXTRA_CHARACTERS = 305,
            MCIERR_DEVICE_NOT_INSTALLED = 306,
            MCIERR_GET_CD = 307,
            MCIERR_SET_CD = 308,
            MCIERR_SET_DRIVE = 309,
            MCIERR_DEVICE_LENGTH = 310,
            MCIERR_DEVICE_ORD_LENGTH = 311,
            MCIERR_NO_INTEGER = 312,

            MCIERR_WAVE_OUTPUTSINUSE = 320,
            MCIERR_WAVE_SETOUTPUTINUSE = 321,
            MCIERR_WAVE_INPUTSINUSE = 322,
            MCIERR_WAVE_SETINPUTINUSE = 323,
            MCIERR_WAVE_OUTPUTUNSPECIFIED = 324,
            MCIERR_WAVE_INPUTUNSPECIFIED = 325,
            MCIERR_WAVE_OUTPUTSUNSUITABLE = 326,
            MCIERR_WAVE_SETOUTPUTUNSUITABLE = 327,
            MCIERR_WAVE_INPUTSUNSUITABLE = 328,
            MCIERR_WAVE_SETINPUTUNSUITABLE = 329,

            MCIERR_SEQ_DIV_INCOMPATIBLE = 336,
            MCIERR_SEQ_PORT_INUSE = 337,
            MCIERR_SEQ_PORT_NONEXISTENT = 338,
            MCIERR_SEQ_PORT_MAPNODEVICE = 339,
            MCIERR_SEQ_PORT_MISCERROR = 340,
            MCIERR_SEQ_TIMER = 341,
            MCIERR_SEQ_PORTUNSPECIFIED = 342,
            MCIERR_SEQ_NOMIDIPRESENT = 343,

            MCIERR_NO_WINDOW = 346,
            MCIERR_CREATEWINDOW = 347,
            MCIERR_FILE_READ = 348,
            MCIERR_FILE_WRITE = 349,
            MCIERR_NO_IDENTITY = 350,
        }
    }

    #endregion

}
